package com.zhxy.util;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Redis工具类，包括读取Redis的配置以及创建连接池。
 * 
 * @author xinyue.zheng
 */
public final class RedisUtils {

    private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);

    private static final String DEFAULT_HOST = "localhost";
    private static final int DEFAULT_PORT = 6379;
    private static final int DEFAULT_TIMEOUT = 60000; // 60 seconds

    // 等待锁时间
    private final static long WAIT_LOCK_TIME = 300;
    private final static String UNLOCK_LUA = "" + "if redis.call(\"get\",KEYS[1]) == ARGV[1] " + "then "
            + "   return redis.call(\"del\",KEYS[1]) " + "else " + "   return 0 " + "end";


    // 连接池
    private static JedisPool pool;

    /**
     * Private constructor.
     */
    private RedisUtils() {
    }

    /**
     * 获取REDIS连接池。
     */
    public synchronized static JedisPool getPool() {
        if ((null != pool) && (!pool.isClosed())) {
            return pool;
        }

        String host = ConfigUtils.getProperty("redis.host");
        if (null == host || host.isEmpty()) {
            host = DEFAULT_HOST;
        }

        String portStr = ConfigUtils.getProperty("redis.port");
        int port = DEFAULT_PORT;
        try {
            port = Integer.parseInt(portStr);
        } catch (Exception e) {
            // 忽略，使用默认端口
            logger.warn(e.getMessage(), e);
        }

        logger.info("Redis Configuration: host={}, port={}", host, port);

        // 连接池配置
        JedisPoolConfig poolConfig = new JedisPoolConfig();

        // 最大连接数, 默认8个
        poolConfig.setMaxTotal(1024);

        // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
        poolConfig.setMinEvictableIdleTimeMillis(1800000);

        // 在获取连接的时候检查有效性, 默认false
        poolConfig.setTestOnBorrow(false);

        // 在空闲时检查有效性, 默认false
        poolConfig.setTestWhileIdle(true);

        pool = new JedisPool(poolConfig, host, port, DEFAULT_TIMEOUT);

        return pool;
    }

    /**
     * 保存key，value
     * @param key
     * @param value
     */
    public static void save(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = getPool().getResource();
            jedis.set(key, value);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (null != jedis) {
                jedis.close();
            }
        }
    }

    /**
     * 根据key获取value
     * @param key
     * @return
     */
    public static String get(String key) {
        Jedis jedis = null;
        try {
            jedis = getPool().getResource();
            return jedis.get(key);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (null != jedis) {
                jedis.close();
            }
        }
        return null;
    }

    /**
     * 批量删除key
     * @param key
     * @return
     */
    public static Long delKey(String[] key) {
        Jedis jedis = null;
        try {
            jedis = getPool().getResource();
            return jedis.del(key);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (null != jedis) {
                jedis.close();
            }
        }
        return null;
    }

    /**
     * 根据pattern获取key
     * @param pattern
     * @return
     */
    public static Set<String> getKeyByPattern(String pattern) {
        Jedis jedis = null;
        try {
            jedis = getPool().getResource();
            return jedis.keys(pattern);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (null != jedis) {
                jedis.close();
            }
        }
        return new HashSet<>();
    }

    /***
     * 如果分布式锁不可用, 当前线程将继续请求, 直到锁可用.
     * 
     * @param lockKey
     *            锁的键值
     * @param timeout
     *            超时时间.单位:毫秒.
     * @return 锁的值.
     */
    public static String lock(String lockKey, long timeout) {
        String lockVal = UUID.randomUUID().toString();

        for (;;) {
            if (!tryLock(lockKey, lockVal, timeout)) {
                try {
                    TimeUnit.MILLISECONDS.sleep(WAIT_LOCK_TIME);
                } catch (InterruptedException e) {
                    logger.error("[RedisLock] sleep error.", e);
                }
            } else {
                break;
            }
        }
        return lockVal;
    }

    /***
     * 尝试获得分布式锁, 如果锁可用, 返回true
     * 
     * @param lockKey
     *            锁的键值
     * @param value
     *            锁的值,推荐使用随机字符.
     * @param timeout
     *            超时时间.单位:毫秒.
     * @return true/false
     */
    public static boolean tryLock(String lockKey, String value, long timeout) {
        Jedis jedis = getPool().getResource();
        try {
            Object result = jedis.set(lockKey, value, "NX", "PX", timeout);
            return "OK".equalsIgnoreCase(result + "");
        } finally {
            jedis.close();
        }
    }

    /***
     * 释放锁.
     * 
     * @param lockKey
     *            锁的键值
     * @param value
     *            锁的值.必须要和上锁的值一致.
     */
    public static void unlock(String lockKey, String value) {
        Jedis jedis = getPool().getResource();
        try {
            jedis.eval(UNLOCK_LUA, 1, lockKey, value);
        } finally {
            jedis.close();
        }
    }

}
